/*
 * Copyright:  2018 smarabbit studio.
 *
 *  Licensed under the Confluent Community License; you may not use this file
 *  except in compliance with the License.  You may obtain a copy of the License at
 *
 *  http://www.confluent.io/confluent-community-license
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations under the License.
 *
 *  @作   者： 黄开晖 (117227773@qq.com)
 *  @日   期:  2021年5月30日
 */
package com.massyframework.beanskin.runtime.framework.installing;

import java.io.File;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.massyframework.beanskin.ModuleIdentifier;
import com.massyframework.beanskin.ModuleRuntimeException;
import com.massyframework.beanskin.SimpleModuleIdentifier;
import com.massyframework.beanskin.util.exceptioncollect.ExceptionCollectUtils;
import com.massyframework.beanskin.util.version.Version;

/**
 * 扫描部署目录下的所有模块
 * @author huangkh
 *
 */
public class ModuleScanner {
	
	private Logger logger = LoggerFactory.getLogger(ModuleScanner.class);
	private Path[] roots;

	/**
	 * 构造方法
	 */
	public ModuleScanner(Path[] roots) {
		this.roots = Objects.requireNonNull(roots, "\"roots\" cannot be null.");
	}

	/**
	 * 扫描模块
	 * @param <B>
	 * @param xmlName {@link String}, 配置文件名称，不包括路径
	 * @param parseHandler  {@link ModuleParser},模块配置解析器
	 * @return {@link Map}
	 */
	public <B> Map<ModuleIdentifier, B> scanning(String xmlName, 
			ModuleParser<B> parseHandler){
		Objects.requireNonNull(xmlName, "\"xmlName\" cannot be null.");
		Objects.requireNonNull(parseHandler, "\"parseHandler\" cannot be null.");
		
		Map<ModuleIdentifier, B> result = new HashMap<>();
		for (Path root: roots) {
			File rootDir = root.toFile();
			this.doScanGroupId(xmlName, rootDir, parseHandler, result);
		}
		return result;
	}
		
	/**
	 * 扫描组编号目录
	 * @param xmlName {@link String},文件名
	 * @param root  {@link File}, 根目录
	 * @param parseHandler {@link Function},处理器
	 * @param parseResult {@link Map}, 收集的解析结果
	 */
	protected <B> void doScanGroupId(String xmlName, File root, 
			ModuleParser<B> parseHandler,  Map<ModuleIdentifier, B> parseResult) {
		File[] childs = root.listFiles();
		for (File child: childs) {
			if (child.isDirectory()) {
				//如果是目录，则有可能是组编号
				this.doScanModuleId(xmlName, child, parseHandler, parseResult);
			}
		}
	}
	
	/**
	 * 扫描模块编号目录
	 * @param <B>
	 * @param xmlName {@link String},文件名
	 * @param groupId {@link File}, 组编号目录
	 * @param parseHandler {@link Function},处理器
	 * @param parseResult {@link Map}, 收集的解析结果
	 */
	protected <B> void doScanModuleId(String xmlName, File groupId, 
			ModuleParser<B> parseHandler, Map<ModuleIdentifier, B> parseResult) {
		File[] childs = groupId.listFiles();
		for (File child: childs) {
			if (child.isDirectory()) {
				//如果是目录，则有可能是模块编号
				this.doScanVersion(xmlName, groupId, child, parseHandler, parseResult);
			}
		}
	}
	
	/**
	 * 扫描版本目录
	 * @param <B>
	 * @param xmlName {@link String},文件名
	 * @param groupId {@link File}, 组编号目录
	 * @param moduleId {@link File},模块编号目录
	 * @param parseHandler {@link Function},处理器
	 * @param parseResult {@link Map}, 收集的解析结果
	 */
	protected <B> void doScanVersion(String xmlName, File groupId,  File moduleId, 
			ModuleParser<B> parseHandler, Map<ModuleIdentifier, B> parseResult) {
		File[] childs = moduleId.listFiles();
		for (File child: childs) {
			if (child.isDirectory()) {
				//如果是目录，则有可能是版本
				this.doParseXmlFile(xmlName, groupId, moduleId, child, parseHandler, parseResult);
			}
		}
	}
	
	/**
	 * 判断文件是否存在并解析
	 * @param <B>
	 * @param xmlName {@link String},文件名
	 * @param groupId {@link File}, 组编号目录
	 * @param moduleId {@link File},模块编号目录
	 * @param parseHandler {@link Function},处理器
	 * @param parseResult {@link Map}, 收集的解析结果
	 */
	protected <B> void doParseXmlFile(String xmlName, File groupId,  File moduleId,  File version,
			ModuleParser<B> parseHandler, Map<ModuleIdentifier, B> result) {
		File xmlFile = new File(version, xmlName);
		if (xmlFile.exists()) {
			ModuleIdentifier identifier = null;
			try {
				identifier = SimpleModuleIdentifier.newBuilder()
					.groupId(groupId.getName())
					.moduleId(moduleId.getName())
					.version(Version.parseVersion(version.getName()))
					.build();
				
				B builder = parseHandler.parse(identifier, xmlFile);
				result.put(identifier, builder);
			}catch(Exception e) {
				if (logger.isErrorEnabled()) {
					logger.error(e.getMessage(), e);
				}
				
				
				if (identifier != null) {
					ExceptionCollectUtils.addException(new ModuleRuntimeException(identifier, e));
				}
				
			}
		}
	}
}
